﻿using System;
using System.Collections.Generic;
using System.Text;
using DevExpress.XtraTreeList;
using System.Windows.Forms;
using System.Drawing;
using DevExpress.XtraTreeList.Columns;
using System.Data;
using DevExpress.XtraTreeList.Nodes;

namespace Tsingshan.RangeSelectSupportor
{
    //TODO:support auto scroll down
    //TODO:add border
    class RangeSelectSupportor
    {
        private TreeList treeList;
        private RangeInfo currRangeInfo = new RangeInfo();
        private List<RangeInfo> rangeInfos = new List<RangeInfo>();
        private bool isCtrlPressed = false;
        private bool isShiftPressed = false;

        public RangeSelectSupportor(TreeList treeList)
        {
            this.treeList = treeList;
            BindEvents();
        }

        public List<DataTable> GetDataTableSelection()
        {
            List<DataTable> selectedDataTables = new List<DataTable>();
            foreach (RangeInfo ri in rangeInfos)
            {
                Console.WriteLine(ri);
                selectedDataTables.Add(GetDataTableRange(ri));
            }
            return selectedDataTables;
        }

        public DataTable GetDataTableRange(RangeInfo ri)
        {
            if (!ri.HasSelection)
                return null;
            DataTable dt = new DataTable(ri.ToString());

            //添加表结构
            foreach (int i in ri.GetColumnIndex())
            {
                TreeListColumn column = GetColumnByVisibleIndex(treeList, i);
                if (column == null)
                    continue;
                DataColumn dc = new DataColumn(column.Caption);
                dt.Columns.Add(dc);
            }

            //数据
            foreach (int i in ri.GetNodeIndex())
            {
                int colIndex = 0;
                DataRow newRow = dt.NewRow();
                TreeListNode node = treeList.GetNodeByVisibleIndex(i);
                foreach (int j in ri.GetColumnIndex())
                {
                    newRow[colIndex++] = node.GetValue(j);
                }
                dt.Rows.Add(newRow);
            }
            return dt;
        }

        public DataTable GetLastSelectDataTableRange()
        {
            return GetDataTableRange(currRangeInfo);
        }

        #region events and Draw
        private void BindEvents()
        {
            this.treeList.MouseDown += new System.Windows.Forms.MouseEventHandler(this.treeList_MouseDown);
            this.treeList.MouseMove += new System.Windows.Forms.MouseEventHandler(this.treeList_MouseMove);
            this.treeList.MouseUp += new System.Windows.Forms.MouseEventHandler(this.treeList_MouseUp);
            this.treeList.CustomDrawNodeCell += new DevExpress.XtraTreeList.CustomDrawNodeCellEventHandler(treeList_CustomDrawNodeCell);
            this.treeList.KeyDown += new System.Windows.Forms.KeyEventHandler(treeList_KeyDown);
            this.treeList.KeyUp += new System.Windows.Forms.KeyEventHandler(treeList_KeyUp);
        }
        
        private void treeList_MouseDown(object sender, MouseEventArgs e)
        {
            TreeList treeList = sender as TreeList;
            TreeListHitInfo hitInfo = treeList.CalcHitInfo(e.Location);
            if (hitInfo.HitInfoType == HitInfoType.Cell)  //当鼠标点击单元格中才进行处理
            {
                if (!isCtrlPressed)
                {
                    rangeInfos.Clear();
                }

                //shift键未被按下，或者currRangeInfo尚未被初始化，则使用新的RangeInfo对象
                //shift键被按下,且currRangeInfo已经被初始化了，则使用上一个RangeInfo的startIndex,
                if (!isShiftPressed || currRangeInfo == null)
                {
                    currRangeInfo = new RangeInfo();
                    currRangeInfo.StartNodeIndex = treeList.GetVisibleIndexByNode(hitInfo.Node);
                    currRangeInfo.StartColumnIndex = hitInfo.Column.VisibleIndex;
                    treeList.Refresh();
                }
                rangeInfos.Add(currRangeInfo);
            }
            else
            {
                currRangeInfo.Reset();
            }
            Console.WriteLine(hitInfo.HitInfoType.ToString());
        }

        private void treeList_MouseMove(object sender, MouseEventArgs e)
        {
            if (e.Button == MouseButtons.Left)
            {
                TreeList treeList = sender as TreeList;
                TreeListHitInfo hitInfo = treeList.CalcHitInfo(e.Location);
                if (currRangeInfo.Available && hitInfo.HitInfoType == HitInfoType.Cell)
                {
                    int endNodeIndex = treeList.GetVisibleIndexByNode(hitInfo.Node);
                    int endColumnIndex = hitInfo.Column.VisibleIndex;
                    //判断是否还在上次重画后的单元格，是则不需要重画
                    if (endColumnIndex != currRangeInfo.EndColumnIndex || endNodeIndex != currRangeInfo.EndNodeIndex)
                    {
                        currRangeInfo.EndNodeIndex = endNodeIndex;
                        currRangeInfo.EndColumnIndex = endColumnIndex;
                        treeList.Refresh();
                    }
                }
            }
        }

        private void treeList_MouseUp(object sender, MouseEventArgs e)
        {
            TreeList treeList = sender as TreeList;
            TreeListHitInfo hitInfo = treeList.CalcHitInfo(e.Location);
            if (currRangeInfo.Available && hitInfo.HitInfoType == HitInfoType.Cell)
            {
                currRangeInfo.EndNodeIndex = treeList.GetVisibleIndexByNode(hitInfo.Node);
                currRangeInfo.EndColumnIndex = hitInfo.Column.VisibleIndex;
                treeList.Refresh();
            }
        }

        void treeList_CustomDrawNodeCell(object sender, DevExpress.XtraTreeList.CustomDrawNodeCellEventArgs e)
        {
            TreeList treeList = sender as TreeList;
            int visibleNodeIndex = treeList.GetVisibleIndexByNode(e.Node);
            int visibelColumnIndex = e.Column.VisibleIndex;
            foreach (RangeInfo info in rangeInfos)
            {
                if (info.ContainsCell(visibleNodeIndex, visibelColumnIndex))
                {
                    e.Appearance.BackColor = Color.BlanchedAlmond;
                    return;
                }
            }
        }
        #endregion

        #region Keys
        void treeList_KeyUp(object sender, System.Windows.Forms.KeyEventArgs e)
        {
            if (e.KeyCode == Keys.ShiftKey)
            {
                isShiftPressed = false;
            }
            if (e.KeyCode == Keys.ControlKey) //control
            {
                isCtrlPressed = false;
            }
        }

        void treeList_KeyDown(object sender, System.Windows.Forms.KeyEventArgs e)
        {
            if (e.KeyCode == Keys.ShiftKey)
            {
                isShiftPressed = true;
            }
            if (e.Control)
            {
                isCtrlPressed = true;
            }
        }
        #endregion

        private TreeListColumn GetColumnByVisibleIndex(TreeList treeList, int visualIndex)
        {
            for (int i = 0; i < treeList.Columns.Count; i++)
            {
                if (treeList.Columns[i].VisibleIndex == visualIndex)
                {
                    return treeList.Columns[i];
                }
            }
            return null;
        }
    }

    public class RangeInfo
    {
        private int startNodeIndex = -1;
        public int StartNodeIndex
        {
            get { return startNodeIndex; }
            set
            {
                this.available = true;
                startNodeIndex = value;
            }
        }

        private int endNodeIndex = -1;
        public int EndNodeIndex
        {
            get { return endNodeIndex; }
            set { endNodeIndex = value; }
        }

        private int startColumnIndex = -1;
        public int StartColumnIndex
        {
            get { return startColumnIndex; }
            set
            {
                this.available = true;
                startColumnIndex = value;
            }
        }

        private int endColumnIndex = -1;
        public int EndColumnIndex
        {
            get { return endColumnIndex; }
            set { endColumnIndex = value; }
        }

        private bool available = false;
        public bool Available
        {
            get { return available; }
        }

        public int RowsCount
        {
            get
            {
                return HasSelection ? Math.Abs(endColumnIndex - startColumnIndex) : 0;
            }
        }

        public int ColumnsCount
        {
            get
            {
                return HasSelection ? Math.Abs(endNodeIndex - startNodeIndex) : 0;
            }
        }

        public IEnumerable<int> GetColumnIndex()
        {
            for (int i = Math.Min(startColumnIndex, endColumnIndex); i <= Math.Max(startColumnIndex, endColumnIndex); i++)
            {
                yield return i;
            }
        }

        public IEnumerable<int> GetNodeIndex()
        {
            for (int i = Math.Min(startNodeIndex, endNodeIndex); i <= Math.Max(startNodeIndex, endNodeIndex); i++)
            {
                yield return i;
            }
        }

        /// <summary>
        /// 判断指定索引的单元格是否在本区域
        /// </summary>
        /// <param name="visibleNodeIndex"></param>
        /// <param name="visibleColumnIndex"></param>
        /// <returns></returns>
        public bool ContainsCell(int visibleNodeIndex, int visibleColumnIndex)
        {
            return HasSelection && IsBetween(visibleNodeIndex, startNodeIndex, endNodeIndex)
                && IsBetween(visibleColumnIndex, startColumnIndex, endColumnIndex);
        }

        /// <summary>
        /// 返回当前区域是否在选区中
        /// </summary>
        public bool HasSelection
        {
            get
            {
                return startColumnIndex >= 0 && startNodeIndex >= 0
                    && endColumnIndex >= 0 && endNodeIndex >= 0;
            }
        }

        public void Reset()
        {
            startNodeIndex = -1;
            endNodeIndex = -1;
            startColumnIndex = -1;
            endColumnIndex = -1;
        }

        private bool IsBetween(int value, int value1, int value2)
        {
            return value >= value1 && value <= value2
                || value <= value1 && value >= value2;
        }

        public override string ToString()
        {
            return string.Format(@"Row:{0}-{1},Col:{2}-{3}", startNodeIndex, endNodeIndex, startColumnIndex, endColumnIndex);
        }
    }
}
